package com.marry.myspringmvc.servlet;

import com.marry.myspringmvc.annotation.*;
import com.marry.myspringmvc.controller.UserController;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author ml
 * @create 2017-08-01--16:07
 */
@WebServlet(name = "dispatcherServlet",urlPatterns = "/*",loadOnStartup = 1,
       initParams = {@WebInitParam(name = "base-package",value = "com.marry.myspringmvc")})
public class DispatcherServlet extends HttpServlet {
    //扫描的基bao
    private String basePackage="";
    //基包下面的所有的带包路径权限类型
    private List<String> packageNames=new ArrayList<String>();
    //注解实例化 注解上的名称：实例化对象
    private Map<String,Object> instanceMap=new HashMap<String, Object>();
    //带包路径的权限定名称：注解上的名称
    private Map<String,String> nameMap=new HashMap<String, String>();
    //url地址和方法的映射关系,springmvc就是方法调用连
    private Map<String,Method> urlMethodMap=new HashMap<String, Method>();
    //method和权限定类名映射关系 主要是为了通过method找到该方法的对象利用反射执行
    private Map<Method,String> methodPackageMap=new HashMap<Method, String>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        //super.init(config);
        basePackage=config.getInitParameter("base-package");

        //1.扫描基bao得到所有的带包路径权限定名
        scanBasePackage(basePackage);
        //2.把带有@Controller/@Service@Repository的类实例化放入Map中，key为注解上的名称
        try {
            instance(packageNames);
            springIOC();
            handlerUrlMethodMap();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        //3.springIOC注入
        //4.完成URL地址与方法的映射关系
    }

    private void handlerUrlMethodMap() throws ClassNotFoundException {

        if(packageNames.size()<1){
            return;
        }
        for(String string:packageNames){
            Class c=Class.forName(string);
            if(c.isAnnotationPresent(Controller.class)){
                Method[] methods=c.getMethods();
                StringBuffer baseUrl=new StringBuffer();
                //类名上的
                if(c.isAnnotationPresent(RequestMapping.class)){
                    RequestMapping requestMapping= (RequestMapping) c.getAnnotation(RequestMapping.class);
                    baseUrl.append(requestMapping.value());

                }
                for(Method method:methods)
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        baseUrl.append(requestMapping.value());
                        urlMethodMap.put(baseUrl.toString(),method);
                        methodPackageMap.put(method,string);
                    }
            }
        }
    }

    private void springIOC() throws IllegalAccessException {

        for(Map.Entry<String,Object> entry:instanceMap.entrySet()){
            Field[] fields=entry.getValue().getClass().getDeclaredFields();

            for(Field field:fields){
                if(field.isAnnotationPresent(Qualifier.class)){
                    String name=field.getAnnotation(Qualifier.class).value();
                    field.setAccessible(true);
                    field.set(entry.getValue(),instanceMap.get(name));
                }
            }
        }
    }

    private void instance(List<String> packageNames) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if(packageNames.size()<1){
            return;
        }
        for(String packageName:packageNames){

            Class c=Class.forName(packageName);
            if(c.isAnnotationPresent(Controller.class)){
                Controller controller= (Controller) c.getAnnotation(Controller.class);
                String controllerName=controller.value();
                if(controllerName==null || "".equals(controllerName)){
                    //如果没写值默认类首字母大写
                    controllerName=toLowerCaseFirstOne(c.getName());
                }
                instanceMap.put(controllerName,c.newInstance());
                nameMap.put(packageName,controllerName);

                System.out.println("controller = " + controller+",value:"+controllerName);
            }else if(c.isAnnotationPresent(Service.class)){
                Service service= (Service) c.getAnnotation(Service.class);
                String serviceName=service.value();
                if(serviceName==null){
                    serviceName=toLowerCaseFirstOne(c.getName());

                }
                instanceMap.put(serviceName,c.newInstance());
                nameMap.put(packageName,serviceName);
                System.out.println("service = " + service+",value:"+serviceName);
            }else if(c.isAnnotationPresent(Repository.class)){
                Repository repository= (Repository) c.getAnnotation(Repository.class);
                String repositoryName=repository.value();
                if(repositoryName==null){
                    repositoryName=toLowerCaseFirstOne(repositoryName);
                }
                instanceMap.put(repositoryName,c.newInstance());
                nameMap.put(packageName,repositoryName);
                System.out.println(" repository = " + repository+",value:"+repositoryName);
            }

        }

    }

    private String toLowerCaseFirstOne(String name) {
        name=name.substring(name.lastIndexOf(".")+1);
        if(Character.isLowerCase(name.charAt(0))){
            return name;
        }else{
            return (new StringBuilder()).append(Character.toLowerCase(name.charAt(0))).append(name.substring(1)).toString();
        }
    }

    private void scanBasePackage(String basePackage) {
        //将basepackage进行转换 将.替换为/
        URL url=this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.","/"));

        File backPackageFile=new File(url.getPath());
        File[] childFiles=backPackageFile.listFiles();
        for(File file:childFiles){
            if(file.isDirectory()){//如果是目录的话递归扫描
                scanBasePackage(basePackage+"."+file.getName());

            }else if(file.isFile()){
                //去掉类名后的class
                packageNames.add(basePackage +"."+file.getName().split("\\.")[0]);

            }
        }

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String url=req.getRequestURI();

        String contextPath=req.getContextPath();
        String path=url.replaceAll(contextPath,"");
        //通过path查找method
        Method method=urlMethodMap.get(path);
        if(method!=null){
            String packageName=methodPackageMap.get(method);
            String controllerName=nameMap.get(packageName);


            UserController userController= (UserController) instanceMap.get(controllerName);

            method.setAccessible(true);
            try {
                method.invoke(userController);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            resp.setCharacterEncoding("utf-8");
            resp.getWriter().println("springmvc模拟");
            resp.getWriter().flush();
            resp.getWriter().close();


        }
    }
}
